package utils

import (
	"fmt"
	"time"
)

type GetInfoCbFunc func(sender interface{}, senderId string, k string, val interface{})

type IMonitorRec interface {
	Calcu(secs float64)
	Reset()
	GetInfo(cb GetInfoCbFunc)
}

type MonitorCenter struct {
	lastCalcu_T    time.Time
	calc_task_dna  int
	sortedlst      []interface{}
	recMap         GSyncMapGroup
	calcu_interval time.Duration
}

var (
	Monitor *MonitorCenter = &MonitorCenter{}
)

type monitor_strorage_rec struct {
	addsn uint32
	id    interface{}
	sn    int
	rec   IMonitorRec
}

func (this *MonitorCenter) CheckStartSpeedTask() {
	if TimeIsUnixZero(this.lastCalcu_T) {

		dna := this.calc_task_dna
		this.calcu_interval = time.Second
		AddTaskAfterInterval("monitor_calcu", time.Second, time.Second, func() bool {
			var secs float64 = 0
			if !TimeIsUnixZero(this.lastCalcu_T) {
				secs = time.Now().Sub(this.lastCalcu_T).Seconds()
			}
			this.lastCalcu_T = time.Now()
			this.recMap.Range(func(key, value interface{}) bool {
				itm := value.(*monitor_strorage_rec)
				itm.rec.Calcu(secs)
				return true
			})
			return dna == this.calc_task_dna
		})
	}
}

func (this *MonitorCenter) CheckStartSpeedTaskEx(calcInterval time.Duration) {
	if this.calcu_interval != calcInterval {
		this.calc_task_dna++
		this.calcu_interval = calcInterval

		dna := this.calc_task_dna
		AddTaskAfterInterval("monitor_calcu", time.Second, calcInterval, func() bool {
			var secs float64 = 0
			if !TimeIsUnixZero(this.lastCalcu_T) {
				secs = time.Now().Sub(this.lastCalcu_T).Seconds()
			}
			this.lastCalcu_T = time.Now()
			this.recMap.Range(func(key, value interface{}) bool {
				itm := value.(*monitor_strorage_rec)
				itm.rec.Calcu(secs)
				return true
			})
			return dna == this.calc_task_dna
		})
	}
}

func (this *MonitorCenter) StopSpeedTask() {
	if !TimeIsUnixZero(this.lastCalcu_T) {
		this.calc_task_dna++
		this.lastCalcu_T = ZeroTime
	}
}

func (this *MonitorCenter) AddMonitor(val interface{}) error {
	if rec, ok := val.(IMonitorRec); ok {
		this.AddMonitorRec(val, rec)
		return nil
	} else {
		return fmt.Errorf("not suport IMonitorRec")
	}
}

func (this *MonitorCenter) AddMonitorRec(id interface{}, rec IMonitorRec) {
	this.recMap.Set(id,
		&monitor_strorage_rec{SNNewUint32(), id, 0, rec})

	this.sortedlst = nil
}

func (this *MonitorCenter) AddMonitorRecEx(id interface{}, sn int, rec IMonitorRec) {
	this.recMap.Set(id,
		&monitor_strorage_rec{SNNewUint32(), id, sn, rec})

	this.sortedlst = nil
}

func (this *MonitorCenter) RemoveMonitorRec(id interface{}) {
	obj, _ := this.recMap.RemoveAndGet(id)
	if obj != nil {
		rec := obj.(*monitor_strorage_rec)
		rec.rec = nil
		rec.id = nil
	}

	this.sortedlst = nil
}

func (this *MonitorCenter) GetInfo(cb GetInfoCbFunc) {
	lst := this.sortedlst
	if lst == nil {
		lst = this.recMap.ListSorted(func(iItm, jItm interface{}) bool {
			irec := iItm.(*monitor_strorage_rec)
			jrec := iItm.(*monitor_strorage_rec)
			return irec.sn < jrec.sn || irec.addsn < jrec.addsn
		})
		this.sortedlst = lst
	}

	if len(lst) == 0 {
		return
	}

	for i := 0; i < len(lst); i++ {
		itm := lst[i].(*monitor_strorage_rec)
		itm.rec.GetInfo(cb)
	}
}

func (this *MonitorCenter) Reset() {
	this.recMap.Range(func(key, value interface{}) bool {
		itm := value.(*monitor_strorage_rec)
		itm.rec.Reset()
		return true
	})
}
